﻿/*
 * Copyright (c) 2015-2016 Constantijn Evenhuis (blueprints@const.nl)
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
using Blueprints.IO;
using Blueprints.Resolution;
using System;
using System.Collections.Generic;

namespace Blueprints
{
    partial class BlueprintContext
    {
        /// <summary>
        /// Throws a BlueprintException for a node that has an invalid value.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="descriptor">The value descriptor for the type.</param>
        /// <param name="content">The content of the node.</param>
        /// <returns>An invalid value.</returns>
        protected override object CreateInvalidValue(NodeInfo node, Type expectedType, IValueDescriptor descriptor, string content)
        {
            throw new BlueprintDocumentException("Error deserializing blueprint: " + DeserializationResult.InvalidValue.ToString() + "\r\n  at " + node.Path, node.Path);
        }

        /// <summary>
        /// Throws a BlueprintException for a node that has a type error.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="result">The deserialization result for this node.</param>
        /// <param name="actualType">The type resolved for this node.</param>
        /// <returns>A type error.</returns>
        protected override object CreateTypeError(NodeInfo node, Type expectedType, DeserializationResult result, Type actualType)
        {
            throw new BlueprintDocumentException("Error deserializing blueprint: " + result.ToString() + "\r\n  at " + node.Path, node.Path);
        }

        /// <summary>
        /// Throws a BlueprintException for a node that is missing one or more required parameters.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="descriptor">The object descriptor for the type.</param>
        /// <param name="parameters">The deserialized constructor parameters, containing one or more null values.</param>
        /// <param name="properties">The deserialized properties.</param>
        /// <returns>An incomplete object.</returns>
        protected override object CreateIncompleteObject(NodeInfo node, Type expectedType, IObjectDescriptor descriptor, object[] parameters, IReadOnlyDictionary<IPropertyDescriptor, object> properties)
        {
            throw new BlueprintDocumentException("Error deserializing blueprint: " + DeserializationResult.MissingRequiredProperty.ToString() + "\r\n  at " + node.Path, node.Path);
        }

        /// <summary>
        /// Throws a BlueprintException for a node that has a link error.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="result">The deserialization result for this node.</param>
        /// <returns>A link error.</returns>
        protected override object CreateLinkError(NodeInfo node, Type expectedType, DeserializationResult result)
        {
            throw new BlueprintDocumentException("Error deserializing blueprint: " + result.ToString() + "\r\n  at " + node.Path, node.Path);
        }

        /// <summary>
        /// Returns a null reference.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <returns>A null reference.</returns>
        protected override object CreateNull(NodeInfo node, Type expectedType)
        {
            return null;
        }

        /// <summary>
        /// Returns the deserialized value of a linked node.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="obj">The deserialized linked node.</param>
        /// <returns>The deserialized linked node.</returns>
        protected override object CreateLink(NodeInfo node, Type expectedType, object obj)
        {
            return obj;
        }

        /// <summary>
        /// Deserializes a value.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="descriptor">The value descriptor for the type.</param>
        /// <param name="content">The content of the node.</param>
        /// <returns>The deserialized value.</returns>
        protected override object CreateValue(NodeInfo node, Type expectedType, IValueDescriptor descriptor, string content)
        {
            return descriptor.ConvertFromString(content);
        }

        /// <summary>
        /// Creates a new collection.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="descriptor">The collection descriptor for the type.</param>
        /// <returns>The created collection.</returns>
        protected override object CreateCollection(NodeInfo node, Type expectedType, ICollectionDescriptor descriptor)
        {
            return descriptor.CreateInstance();
        }

        /// <summary>
        /// Sets the elements of a collection.
        /// </summary>
        /// <param name="instance">The collection to add the elements to.</param>
        /// <param name="descriptor">The collection descriptor for the type.</param>
        /// <param name="elements">The deserialized elements to add.</param>
        protected override void SetCollectionElements(object instance, ICollectionDescriptor descriptor, IEnumerable<object> elements)
        {
            foreach (object element in elements)
            {
                descriptor.Add(instance, element);
            }
        }

        /// <summary>
        /// Creates a new array.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="descriptor">The array descriptor for the type.</param>
        /// <param name="elements">The deserialized elements to add.</param>
        /// <returns>The created array.</returns>
        protected override object CreateArray(NodeInfo node, Type expectedType, IArrayDescriptor descriptor, IReadOnlyList<object> elements)
        {
            return descriptor.CreateInstance(elements);
        }

        /// <summary>
        /// Creates a new object instance.
        /// </summary>
        /// <param name="node">The node that was deserialized.</param>
        /// <param name="expectedType">The type expected by the parent object.</param>
        /// <param name="descriptor">The object descriptor for the type.</param>
        /// <param name="parameters">The deserialized constructor parameters.</param>
        /// <returns>The created object instance.</returns>
        protected override object CreateObject(NodeInfo node, Type expectedType, IObjectDescriptor descriptor, object[] parameters)
        {
            try
            {
                return descriptor.CreateInstance(parameters);
            }
            catch (BlueprintException bex)
            {
                throw new BlueprintDocumentException(bex, node.Path);
            }
        }

        /// <summary>
        /// Sets the properties of an object.
        /// </summary>
        /// <param name="instance">The object instance of which to set the properties.</param>
        /// <param name="properties">The deserialized properties to set.</param>
        protected override void SetObjectProperties(object instance, IReadOnlyDictionary<IPropertyDescriptor, object> properties)
        {
            foreach (KeyValuePair<IPropertyDescriptor, object> property in properties)
            {
                property.Key.SetValue(instance, property.Value);
            }
        }
    }
}